package org.jsola.hr.common.oneKeyPay;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import kong.unirest.HttpResponse;
import kong.unirest.Unirest;
import lombok.extern.slf4j.Slf4j;
import okhttp3.HttpUrl;
import org.jsola.common.StrKit;
import org.jsola.exception.ParamException;
import org.jsola.hr.vo.payWx.VxBatchOrderVO;
import org.jsola.hr.vo.payWx.VxBatchResponseVO;
import org.jsola.hr.vo.payWx.VxBuildUserToken;
import org.jsola.hr.vo.payWx.checkFace.VxCheckFaceOrderVO;
import org.jsola.hr.vo.payWx.checkFace.VxCheckFaceReturnVO;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.cert.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;

/**
 * 微信批量支付相关方法
 *
 * @author wu
 */
@Slf4j
public class VXpayClear {

    /**
     * 认证类型，目前固定为WECHATPAY2-SHA256-RSA2048
     */
    private final static String SCHEMA = "WECHATPAY2-SHA256-RSA2048";

    public static void main(String[] args) {
        String name = rsaEncryptOAEP("sw", "D:\\payWX\\wx_key\\wechatpay_249422CB735324ABDD7B9E0800217510B922B483.pem");
        String idCard = rsaEncryptOAEP("13221", "D:\\payWX\\wx_key\\wechatpay_249422CB735324ABDD7B9E0800217510B922B483.pem");
        String wechatpaySerial = "249422CB735324ABDD7B9E0800217510B922B483";
        String authorization = generateAuthorization("{\n" +
                        "  \"openid\": \"oKK1a5OaGZyks00kCVcCPEE-QYaQ\",\n" +
                        "  \"appid\": \"wx3100093fc95f343f\",\n" +
                        "  \"sub_mchid\":\"1615112360\",\n" +
                        "  \"user_name\":\""+name+"\",\n" +
                        "  \"id_card_number\":\""+idCard+"\",\n" +
                        "  \"employment_type\":\"LONG_TERM_EMPLOYMENT\",\n" +
                        "  \"authenticate_number\": \"zxr202111290003\",\n" +
                        "  \"project_name\": \"职薪人\",\n" +
                        "  \"employer_name\": \"京普科技\"\n" +
                        "}",
                "POST",
                "https://api.mch.weixin.qq.com/v3/payroll-card/authentications/pre-order-with-auth",
                "1579540071",
                "3E90DFBC59C06337C19C9124B353C468051A88AF",
                "D:\\payWX\\服务商相关\\1579540071_20211020_cert\\apiclient_key.pem");

        HttpResponse<String> socResponse = Unirest.post("https://api.mch.weixin.qq.com/v3/payroll-card/authentications/pre-order-with-auth")
                .header("Content-Type", "application/json")
                .header("Authorization", authorization)
                .header("User-Agent", "zhixinren/1.0")
                .header("Accept", "*/*")
                .header("Wechatpay-Serial", wechatpaySerial)
                .body("{\n" +
                        "  \"openid\": \"oKK1a5OaGZyks00kCVcCPEE-QYaQ\",\n" +
                        "  \"appid\": \"wx3100093fc95f343f\",\n" +
                        "  \"sub_mchid\":\"1615112360\",\n" +
                        "  \"user_name\":\""+name+"\",\n" +
                        "  \"id_card_number\":\""+idCard+"\",\n" +
                        "  \"employment_type\":\"LONG_TERM_EMPLOYMENT\",\n" +
                        "  \"authenticate_number\": \"zxr202111290003\",\n" +
                        "  \"project_name\": \"职薪人\",\n" +
                        "  \"employer_name\": \"京普科技\"\n" +
                        "}")
                .asString();

        log.info("创建微信批次订单返回结果:{}", socResponse.getBody());
    }

    /**
     * 微工卡生成授权token
     * @param vxBuildUserToken  参数body
     * @param authorization   微信接口的请求头  Authorization
     * @param wechatpaySerial 微信平台证书的序列号
     * @return 微工卡生成授权token的返回值
     */
    public static VxCheckFaceReturnVO vxBuildAuthToken(VxBuildUserToken vxBuildUserToken, String authorization, String wechatpaySerial) {
        HttpResponse<String> socResponse = Unirest.post("https://api.mch.weixin.qq.com/v3/payroll-card/tokens")
                .header("Content-Type", "application/json")
                .header("Authorization", authorization)
                .header("User-Agent", "zhixinren/1.0")
                .header("Accept", "*/*")
                .header("Wechatpay-Serial", wechatpaySerial)
                .body(JSON.toJSONString(vxBuildUserToken))
                .asString();
        log.info("微工卡生成授权token返回结果:{},===入参:{}", socResponse.getBody(), JSONObject.toJSONString(vxBuildUserToken));
        VxCheckFaceReturnVO vxCheckFaceReturnVO = JSON.parseObject(socResponse.getBody(), VxCheckFaceReturnVO.class);
        if (socResponse.isSuccess()) {
            return vxCheckFaceReturnVO;
        } else {
            log.error("微工卡生成授权token失败，微信返回结果：{}", socResponse.getBody());
            throw new ParamException("微工卡生成授权token失败，微信返回结果: " + vxCheckFaceReturnVO.getMessage());
        }
    }


    /**
     * 微信人脸核身预下单接口
     * @param vxCheckFaceOrderVO  参数body
     * @param authorization   微信接口的请求头  Authorization
     * @param wechatpaySerial 微信平台证书的序列号
     * @return 人脸核身预下单的返回值
     */
    public static VxCheckFaceReturnVO vxCheckFace(VxCheckFaceOrderVO vxCheckFaceOrderVO, String authorization, String wechatpaySerial) {
        HttpResponse<String> socResponse = Unirest.post("https://api.mch.weixin.qq.com/v3/payroll-card/authentications/pre-order-with-auth")
                .header("Content-Type", "application/json")
                .header("Authorization", authorization)
                .header("User-Agent", "zhixinren/1.0")
                .header("Accept", "*/*")
                .header("Wechatpay-Serial", wechatpaySerial)
                .body(JSON.toJSONString(vxCheckFaceOrderVO))
                .asString();
        log.info("创建微信人脸核身预下单返回结果:{},===入参:{}", socResponse.getBody(), JSONObject.toJSONString(vxCheckFaceOrderVO));
        VxCheckFaceReturnVO vxCheckFaceReturnVO = JSON.parseObject(socResponse.getBody(), VxCheckFaceReturnVO.class);
        if (socResponse.isSuccess()) {
            return vxCheckFaceReturnVO;
        } else {
            log.error("创建微信人脸核身预下单失败，微信返回结果：{}", socResponse.getBody());
            throw new ParamException("微信人脸核身失败，微信返回结果: " + vxCheckFaceReturnVO.getMessage());
        }
    }


    /**
     * 发起微信批次下单(会返回 微信内部的支付批次单号)
     * @param vxBatchOrderVO 参数body
     * @param authorization 微信接口的请求头  Authorization
     * @param wechatpaySerial 微信平台证书的序列号
     * @return 微信批次下单的返回值（微信内部的支付批次单号）
     */
    public static VxBatchResponseVO vxBatchStart(VxBatchOrderVO vxBatchOrderVO, String authorization, String wechatpaySerial) {
        HttpResponse<String> socResponse = Unirest.post("https://api.mch.weixin.qq.com/v3/partner-transfer/batches")
                .header("Content-Type", "application/json")
                .header("Authorization", authorization)
                .header("User-Agent", "zhixinren/1.0")
                .header("Accept", "*/*")
                .header("Wechatpay-Serial", wechatpaySerial)
                .body(JSON.toJSONString(vxBatchOrderVO))
                .asString();
        log.info("批次ID为:{}创建微信批次订单返回结果:{}入参:{}",
                vxBatchOrderVO.getOutBatchNo(), socResponse.getBody(), JSONObject.toJSONString(vxBatchOrderVO));
        if (socResponse.isSuccess()) {
            return JSON.parseObject(socResponse.getBody(), VxBatchResponseVO.class);
        } else {
            log.error("批次ID为:{}创建微信批次订单失败，微信返回结果：{}", vxBatchOrderVO.getOutBatchNo(), socResponse.getBody());
            throw new ParamException("创建微信批次订单失败，微信返回结果: " + socResponse.getBody());
        }
    }

    /**
     * 根据url查询微信的相关信息（批次订单的结果和余额信息都可以查询）
     * @param getRul GET请求的URL
     * @param authorization 请求头Authorization
     * @param wechatpaySerial 微信平台证书的序列号
     * @return 查询的结果
     */
    public static String vxSelectTransferAccountsInfo(String getRul, String authorization, String wechatpaySerial) {
        HttpResponse<String> socResponse = Unirest.get(getRul)
                .header("Content-Type", "application/json")
                .header("Authorization", authorization)
                .header("User-Agent", "zhixinren/1.0")
                .header("Accept", "*/*")
                .header("Wechatpay-Serial", wechatpaySerial)
                .asString();
        log.info("查询微信，微信返回结果：{}", socResponse.getBody());
        if (socResponse.isSuccess()) {
            return socResponse.getBody();
        } else {
            log.error("查询微信信息失败，微信返回结果：{},url:{},authorization:{},wechatpaySerial:{}",
                    socResponse.getBody(), getRul, authorization, wechatpaySerial);
            throw new ParamException("查询微信信息失败，微信返回结果："
                    + socResponse.getBody()
                    + "请联系管理员查询具体原因！");
        }
    }

    /**
     * 生成调用微信接口的请求头  Authorization
     * @param body 请求参数 没有可以传"";
     * @param method 请求方式
     * @param url 请求url
     * @param merchantId 服务商的商户号
     * @param certificateSerialNo 服务商的证书序列号
     * @param filePath 生成服务商的key的路径
     * @return Authorization
     */
    public static String generateAuthorization(String body, String method, String url,
                                        String merchantId, String certificateSerialNo, String filePath) {
        if (StrKit.isEmpty(method) || StrKit.isEmpty(url) || StrKit.isEmpty(merchantId) ||
                StrKit.isEmpty(certificateSerialNo) || StrKit.isEmpty(filePath)) {
            throw new ParamException("参数都不能为空");
        }
        String nonceStr = System.currentTimeMillis() + "whahy";
        long timestamp = System.currentTimeMillis() / 1000;
        String message = buildMessage(method, HttpUrl.parse(url), timestamp, nonceStr, body);
        String signature = sign(message.getBytes(StandardCharsets.UTF_8), filePath);

        return SCHEMA + "\n"
                + "mchid=\"" + merchantId + "\","
                + "nonce_str=\"" + nonceStr + "\","
                + "timestamp=\"" + timestamp + "\","
                + "serial_no=\"" + certificateSerialNo + "\","
                + "signature=\"" + signature + "\"";
    }

    public static String buildMessage(String method, HttpUrl url, long timestamp, String nonceStr, String body) {
        String canonicalUrl = url.encodedPath();
        if (url.encodedQuery() != null) {
            canonicalUrl += "?" + url.encodedQuery();
        }

        return method + "\n"
                + canonicalUrl + "\n"
                + timestamp + "\n"
                + nonceStr + "\n"
                + body + "\n";
    }

    /**
     * 生成签名值
     */
    public static String sign(byte[] message, String filePath) {

        try {
            Signature sign = Signature.getInstance("SHA256withRSA");
            sign.initSign(getPrivateKey(filePath));
            sign.update(message);
            return Base64.getEncoder().encodeToString(sign.sign());
        } catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) {
            e.printStackTrace();
            log.error("生成签名值失败，失败原因:{}", e.getMessage());
            throw new ParamException("生成签名值失败，失败原因：" + e.getMessage());
        }

    }

    /**
     * 获取私钥。
     *
     * @param filename 私钥文件路径  (required)
     * @return 私钥对象
     */
    public static PrivateKey getPrivateKey(String filename) {
        try {
            String content = new String(Files.readAllBytes(Paths.get(filename)), StandardCharsets.UTF_8);
            String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
                    .replace("-----END PRIVATE KEY-----", "")
                    .replaceAll("\\s+", "");

            KeyFactory kf = KeyFactory.getInstance("RSA");
            return kf.generatePrivate(
                    new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));
        } catch (NoSuchAlgorithmException | IOException e) {
            throw new ParamException("当前Java环境不支持RSA" + e.getMessage());
        } catch (InvalidKeySpecException e) {
            throw new ParamException("无效的密钥格式");
        }
    }


    /**
     * 通过证书加密敏感信息
     *
     * @param message 需要加密的信息
     * @param filePath 微信平台的证书路径
     * @return 加密后的str
     */
    public static String rsaEncryptOAEP(String message, String filePath) {
        try {
            X509Certificate certificate = getCertificate(filePath);
            Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
            cipher.init(Cipher.ENCRYPT_MODE, certificate.getPublicKey());

            byte[] data = message.getBytes(StandardCharsets.UTF_8);
            byte[] cipherdata = cipher.doFinal(data);
            return Base64.getEncoder().encodeToString(cipherdata);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            throw new RuntimeException("当前Java环境不支持RSA v1.5/OAEP", e);
        } catch (InvalidKeyException e) {
            throw new IllegalArgumentException("无效的证书", e);
        } catch (IllegalBlockSizeException | BadPaddingException e) {
            throw new ParamException("加密原串的长度不能超过214字节");
        } catch (IOException e) {
            e.printStackTrace();
            throw new ParamException("IO异常");
        }
    }

    /**
     * 开发者应使用商户私钥对下行的敏感信息的密文进行解密
     * @param cipherText 加密字符串
     * @param privateKeyPath 服务商私钥路径
     * @return 解密后的字符串
     */
    public static String rsaDecryptOAEP(String cipherText, String privateKeyPath)
            throws BadPaddingException {
        PrivateKey privateKey = getPrivateKey(privateKeyPath);
        try {
            Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
            cipher.init(Cipher.DECRYPT_MODE, privateKey);

            byte[] data = Base64.getDecoder().decode(cipherText);
            return new String(cipher.doFinal(data), StandardCharsets.UTF_8);
        } catch (NoSuchPaddingException | NoSuchAlgorithmException e) {
            throw new RuntimeException("当前Java环境不支持RSA v1.5/OAEP", e);
        } catch (InvalidKeyException e) {
            throw new IllegalArgumentException("无效的私钥", e);
        } catch (BadPaddingException | IllegalBlockSizeException e) {
            throw new BadPaddingException("解密失败");
        }
    }

    /**
     * 获取微信平台的证书。
     *
     * @param filename 微信平台的证书路径
     * @return X509证书
     */
    public static X509Certificate getCertificate(String filename) throws IOException {
        InputStream fis = new FileInputStream(filename);
        try (BufferedInputStream bis = new BufferedInputStream(fis)) {
            CertificateFactory cf = CertificateFactory.getInstance("X509");
            X509Certificate cert = (X509Certificate) cf.generateCertificate(bis);
            cert.checkValidity();
            return cert;
        } catch (CertificateExpiredException e) {
            throw new RuntimeException("证书已过期", e);
        } catch (CertificateNotYetValidException e) {
            throw new RuntimeException("证书尚未生效", e);
        } catch (CertificateException e) {
            throw new RuntimeException("无效的证书文件", e);
        }
    }


}
